//
// Created by suhuamo on 25-8-13.
//

#ifndef UTIL_H
#define UTIL_H

#include <map>

using namespace std;

// 消息的类型
enum class RTMP_MESSAGE_TYPE: uint8_t {
    SET_CHUNK_SIZE = 0x01,
    USER_CONTROL = 0x04,
    WINDOW_ACK_SIZE = 0x05,
    SET_PEER_BANDWIDTH = 0x06,
    AUDIO = 0x08,
    VIDEO = 0x09,
    NOTIFY = 0x12,
    COMMAND = 0x14
};

// Chunk的类型
enum class RTMP_CHUNK_ID: uint16_t {
    USER_CONTROL = 0x02,
    COMMAND = 0x03,
    AUDIO = 0x04,
    ON_STATUS = 0x05,
    VIDEO = 0x06,
};

// userControl的类型
enum class RTMP_USER_CONTROL: uint16_t {
    STREAM_BEGIN = 0x00,
    SET_BUFFER_LENGTH = 0x03,
    STREAM_IS_RECORDED = 0x04,
};

// AMF0 类型定义
enum class AMF0Type : uint8_t {
    NUMBER = 0,
    BOOLEAN = 1,
    STRING = 2,
    OBJECT = 3,
    NULL_MARKER = 5,
    UNDEFINED = 6,
    ECMA_ARRAY = 8,
    OBJECT_END = 9
};

struct RtmpHeader {
    uint8_t fmt = 0; // 低两位
    uint16_t csId = 0; // 高五位【或者多加一个字节，或者多加两个字节】
    uint32_t timestamp = 0;// 三个字节
    uint32_t timestampDelta = 0;// 三个字节,和 timestamp 不会同时存在
    uint32_t messageLength = 0;//三个字节
    uint8_t messageType = 0;//一个字节
    uint32_t messageStreamId = 0;//四个字节【流的id，每一个视频会有一个流id，由流id来确定是哪个视频；在CreateStream响应的时候会创建的一个新的流id,之后每次响应的时候messageStreamId就应该使用这个了。创建之前使用0即可，表示流没有创建】
    // uint32_t extendedTimestamp;// 简单开发不考虑需要扩展时间字段的情况，实际开发大部分情况下也不会遇到这种情况
    int idx = 0;    //记录当前应该使用buf数组的哪一个字节
    void print() const {
        printf("ChunkHeader: \n"\
            "fmt:%u\n"\
            "csId:%u\n"\
            "timestamp:%u\n"\
            "messageLength:%u\n"\
            "messageType:%u\n"\
            "messageStreamId:%u\n"
            "======================\n", fmt, csId, timestamp, messageLength, messageType, messageStreamId);
    }
};

class Buffer
{
private:
    // 用于存放数据
    std::string buf_;
public:
    Buffer() {}
    ~Buffer() {}
    // 将数据追加到buf中
    void Append(const char *data, size_t size) {
        buf_.append(data, size);
    }
    // 返回buf的大小
    size_t Size() {
        return buf_.size();
    }
    // 返回buf的首地址
    const char* Data() {
        return buf_.data();
    }
    // 清空buf
    void Clear() {
        buf_.clear();
    }
    // 从buf的pos位置开始，删除nn个字节。pos的起始位置是0开始的
    void Erase(size_t pos, size_t nn) {
        buf_.erase(pos, nn);
    }

};

// AMF0 编码器
class AMF0Encoder {
public:
    AMF0Encoder() {}

    void writeNumber(double value) {
        buffer_.push_back(static_cast<uint8_t>(AMF0Type::NUMBER));
        uint64_t intRepr;
        memcpy(&intRepr, &value, sizeof(double));
        for (int i = 7; i >= 0; i--) {
            buffer_.push_back(static_cast<uint8_t>((intRepr >> (i * 8)) & 0xFF));
        }
    }

    void writeBoolean(bool value) {
        buffer_.push_back(static_cast<uint8_t>(AMF0Type::BOOLEAN));
        buffer_.push_back(value ? 0x01 : 0x00);
    }

    void writeString(const std::string& str) {
        buffer_.push_back(static_cast<uint8_t>(AMF0Type::STRING));
        uint16_t len = static_cast<uint16_t>(str.length());
        buffer_.push_back(static_cast<uint8_t>(len >> 8));
        buffer_.push_back(static_cast<uint8_t>(len & 0xFF));
        buffer_.insert(buffer_.end(), str.begin(), str.end());
    }

    void writeNull() {
        buffer_.push_back(static_cast<uint8_t>(AMF0Type::NULL_MARKER));
    }

    void writeObjectBegin() {
        buffer_.push_back(static_cast<uint8_t>(AMF0Type::OBJECT));
    }

    void writeObjectEnd() {
        buffer_.push_back(0x00);
        buffer_.push_back(0x00);
        buffer_.push_back(static_cast<uint8_t>(AMF0Type::OBJECT_END));
    }

    void writeObjectName(const std::string& str) {
        uint16_t len = static_cast<uint16_t>(str.length());
        buffer_.push_back(static_cast<uint8_t>(len >> 8));
        buffer_.push_back(static_cast<uint8_t>(len & 0xFF));
        buffer_.insert(buffer_.end(), str.begin(), str.end());
    }

    void writeObjectNumber(const std::string& name, double value) {
        writeObjectName(name);
        writeNumber(value);
    }

    void writeObjectString(const std::string& name, const std::string& value) {
        writeObjectName(name);
        writeString(value);
    }

    void writeObjectBoolean(const std::string& name, bool value) {
        writeObjectName(name);
        writeBoolean(value);
    }

    void writeObject(const map<std::string, std::string>& properties) {
        buffer_.push_back(static_cast<uint8_t>(AMF0Type::OBJECT));
        for (const auto& [key, value] : properties) {
            // 写入key (UTF-8字符串)
            uint16_t keyLen = htons(static_cast<uint16_t>(key.length()));
            buffer_.push_back(static_cast<uint8_t>(keyLen >> 8));
            buffer_.push_back(static_cast<uint8_t>(keyLen & 0xFF));
            buffer_.insert(buffer_.end(), key.begin(), key.end());

            // todo:这里不一定是字符串，也可能是Number、Boolean
            // 写入value (作为字符串)
            writeString(value);
        }
        // 对象结束标记
        buffer_.push_back(0x00);
        buffer_.push_back(0x00);
        buffer_.push_back(static_cast<uint8_t>(AMF0Type::OBJECT_END));
    }

    const std::vector<uint8_t>& getData() const {
        return buffer_;
    }

private:
    std::vector<uint8_t> buffer_;
};

// AMF0 解码器
class AMF0Decoder {
public:
    AMF0Decoder(const uint8_t* data, size_t len)
        : data_(data), len_(len), pos_(0) {}

    AMF0Type readType() {
        return static_cast<AMF0Type>(readByte());
    }

    double readNumber() {
        if (readType() != AMF0Type::NUMBER) {
            throw std::runtime_error("Expected number type");
        }

        uint64_t value = 0;
        for (int i = 0; i < 8; i++) {
            value = (value << 8) | readByte();
        }

        double result;
        memcpy(&result, &value, sizeof(double));
        return result;
    }

    bool readBoolean() {
        if (readType() != AMF0Type::BOOLEAN) {
            throw std::runtime_error("Expected boolean type");
        }
        return readByte() != 0;
    }

    std::string readString() {
        AMF0Type type = readType();
        if (type != AMF0Type::STRING) {
            throw std::runtime_error("Expected string type");
        }

        uint16_t len = (readByte() << 8) | readByte();
        std::string result;
        for (int i = 0; i < len; i++) {
            if (data_[pos_] == 0xC3) {
                pos_++;
                len_++;
            }
            // fix me：都怪ffplay，它发送的字符串有问题，可能会多C3
            result.push_back(static_cast<char>(readByte()));
        }
        return result;
    }

    std::map<std::string, std::string> readObject() {
        if (readType() != AMF0Type::OBJECT) {
            throw std::runtime_error("Expected object type");
        }

        std::map<std::string, std::string> result;
        while (true) {
            // 读取key长度
            uint16_t keyLen = (readByte() << 8) | readByte();
            if (keyLen == 0) {
                // 检查结束标记
                uint8_t endMarker = readByte();
                // fix me: 离谱，为什么会在接受到了 3C 的情况下，这个结束标记就变成了 00 00 5C了
                // if (endMarker != static_cast<uint8_t>(AMF0Type::OBJECT_END)) {
                //     throw std::runtime_error("Invalid object end marker");
                // }
                break;
            }

            // 读取key
            std::string key;
            for (int i = 0; i < keyLen; i++) {
                if (data_[pos_] == 0xC3) {
                    pos_++;
                    len_++;
                }
                // fix me：都怪ffplay，它发送的字符串有问题，可能会多C3
                key.push_back(static_cast<char>(readByte()));
            }

            // 读取value (作为字符串)
            auto type = static_cast<AMF0Type>(data_[pos_]);
            switch (type) {
                case AMF0Type::STRING:
                    result[key] = readString();
                    break;
                case AMF0Type::NUMBER:
                    result[key] = std::to_string(readNumber());
                    break;
                case AMF0Type::BOOLEAN:
                    result[key] = std::to_string(readBoolean());
                    break;
                default:
                    break;
            }


        }
        return result;
    }

    void skipNull() {
        if (readType() != AMF0Type::NULL_MARKER) {
            throw std::runtime_error("Expected null marker");
        }
    }

private:
    uint8_t readByte() {
        if (pos_ >= len_) {
            throw std::out_of_range("Attempt to read beyond buffer");
        }
        return data_[pos_++];
    }

    const uint8_t* data_;
    size_t len_;
    size_t pos_;
};

#endif //UTIL_H
